﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace FortDoneQuicker
{
    public enum KeyboardKey { d, leftArrow, rightArrow, downArrow, upArrow, enter };

    class Keyboard
    {
        private enum KeyboardMsg { WM_KEYDOWN = 0x100, WM_KEYUP = 0x101, WM_CHAR = 0x102, WM_SYSKEYDOWN = 0x104, WM_SYSKEYUP = 0x105 };
        private Dictionary<Tuple<KeyboardKey, KeyboardMsg>, KeyInfo> keyInfoTable = new Dictionary<Tuple<KeyboardKey, KeyboardMsg>, KeyInfo>();
        private class KeyInfo
        {
            public IntPtr wParam;
            public IntPtr lParam;
        }

        private void addKeyInfo(KeyboardKey key, Keys virtKey, int scanCode, bool isExtendedKey)
        {
            KeyInfo infoDown = new KeyInfo();
            infoDown.wParam = (IntPtr)virtKey;
            infoDown.lParam = createKeyDownLParam(scanCode, isExtendedKey);

            KeyInfo infoUp = new KeyInfo();
            infoUp.wParam = (IntPtr)virtKey;
            infoUp.lParam = createKeyUpLParam(scanCode, isExtendedKey);

            keyInfoTable.Add(new Tuple<KeyboardKey, KeyboardMsg>(key, KeyboardMsg.WM_KEYDOWN), infoDown);
            keyInfoTable.Add(new Tuple<KeyboardKey, KeyboardMsg>(key, KeyboardMsg.WM_KEYUP), infoUp);
        }
        private void addKeyInfo(KeyboardKey key, Keys virtKey, int charCode, int scanCode, bool isExtendedKey, bool isAltDown)
        {
            KeyInfo infoDown = new KeyInfo();
            infoDown.wParam = (IntPtr)virtKey;
            infoDown.lParam = createKeyDownLParam(scanCode, isExtendedKey);

            KeyInfo infoUp = new KeyInfo();
            infoUp.wParam = (IntPtr)virtKey;
            infoUp.lParam = createKeyUpLParam(scanCode, isExtendedKey);

            KeyInfo infoChar = new KeyInfo();
            infoChar.wParam = (IntPtr)charCode;
            infoChar.lParam = createCharLParam(scanCode, isExtendedKey, isAltDown);

            keyInfoTable.Add(new Tuple<KeyboardKey, KeyboardMsg>(key, KeyboardMsg.WM_KEYDOWN), infoDown);
            keyInfoTable.Add(new Tuple<KeyboardKey, KeyboardMsg>(key, KeyboardMsg.WM_KEYUP), infoUp);
            keyInfoTable.Add(new Tuple<KeyboardKey, KeyboardMsg>(key, KeyboardMsg.WM_CHAR), infoChar);
        }

        private IntPtr createKeyDownLParam(int scanCode, bool isExtendedKey)
        {
            int repeat = 0x01;
            int scan = (scanCode << 16);
            int extended = (isExtendedKey) ? (0x01 << 24) : (0x00 << 24);
            int context = (0x00 << 29);
            int prevState = (0x00 << 30);
            int transition = (0x00 << 31);

            return (IntPtr)(repeat + scan + extended + context + prevState + transition);
        }
        private IntPtr createKeyUpLParam(int scanCode, bool isExtendedKey)
        {
            int repeat = 0x01;
            int scan = (scanCode << 16);
            int extended = (isExtendedKey) ? (0x01 << 24) : (0x00 << 24);
            int context = (0x00 << 29);
            int prevState = (0x01 << 30);
            int transition = (0x01 << 31);

            return (IntPtr)(repeat + scan + extended + context + prevState + transition);
        }
        private IntPtr createCharLParam(int scanCode, bool isExtendedKey, bool isAltDown)
        {
            int repeat = 0x01;
            int scan = (scanCode << 16);
            int extended = (isExtendedKey) ? (0x01 << 24) : (0x00 << 24);
            int context = (isAltDown) ? (0x01 << 29) : (0x00 << 29);
            int prevState = (0x00 << 30);
            int transition = (0x00 << 31);

            return (IntPtr)(repeat + scan + extended + context + prevState + transition);
        }

        private static Keyboard instance = null;
        public static Keyboard Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Keyboard();
                }
                return instance;
            }
        }

        private Keyboard()
        {
            init();
        }
        private void init()
        {
            addKeyInfo(KeyboardKey.d, Keys.D, 0x64, 0x20, false, false);

            addKeyInfo(KeyboardKey.leftArrow, Keys.Left, 0x4B, false);
            addKeyInfo(KeyboardKey.rightArrow, Keys.Right, 0x4D, false);
            addKeyInfo(KeyboardKey.downArrow, Keys.Down, 0x50, false);
            addKeyInfo(KeyboardKey.upArrow, Keys.Up, 0x48, false);

            addKeyInfo(KeyboardKey.enter, Keys.Enter, 0x0D, 0x1C, false, false);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
        public void keyDown(int hWnd, KeyboardKey k)
        {
            KeyInfo info = new KeyInfo();

            if (keyInfoTable.TryGetValue(new Tuple<KeyboardKey, KeyboardMsg>(k, KeyboardMsg.WM_KEYDOWN), out info))
            {
                Console.WriteLine(k + " down");
                SendMessage((IntPtr)hWnd, (uint)KeyboardMsg.WM_KEYDOWN, info.wParam, info.lParam);
            }
            if (keyInfoTable.TryGetValue(new Tuple<KeyboardKey, KeyboardMsg>(k, KeyboardMsg.WM_CHAR), out info))
            {
                SendMessage((IntPtr)hWnd, (uint)KeyboardMsg.WM_CHAR, info.wParam, info.lParam);
            }
        }
        public void keyUp(int hWnd, KeyboardKey k)
        {
            KeyInfo info = new KeyInfo();

            if (keyInfoTable.TryGetValue(new Tuple<KeyboardKey, KeyboardMsg>(k, KeyboardMsg.WM_KEYUP), out info))
            {
                Console.WriteLine(k + " up");
                SendMessage((IntPtr)hWnd, (uint)KeyboardMsg.WM_KEYUP, info.wParam, info.lParam);
            }
        }
        public void keyTap(int hWnd, KeyboardKey k)
        {
            keyDown(hWnd, k);
            keyUp(hWnd, k);
        }
    }
}
